/*
* ieee1394io.cc -- asynchronously grabbing DV data
* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
* Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
* Copyright (C) 2007 Stéphane Brunner <stephane.brunner@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifndef _IEEE1394IO_H
#define _IEEE1394IO_H 1

#include <libraw1394/raw1394.h>
#include <libraw1394/csr.h>
#ifdef HAVE_IEC61883
#include <libiec61883/iec61883.h>
#endif
#ifdef HAVE_DV1394
#include "dv1394.h"
#endif

#include <string>
using std::string;
#include <deque>
using std::deque;

class Frame;

class IEEE1394
{
protected:
	// The list of available frames
//	deque < Frame* > frames;
	// Define the size of the buffer
#define BUFFERED_FRAMES	20
	Frame *frames[ BUFFERED_FRAMES ];
	// Buffer and positional information
	int framePosition;


public:
	IEEE1394();
	~IEEE1394();
	
	void DoneWithFrame( Frame * );
};

class IEEE1394Reader : public IEEE1394
{
protected:
	/// a list of already received frames
//	deque < Frame* > outFrames;

public:

	IEEE1394Reader( int chn = 63, int frames = 50 );
	virtual ~IEEE1394Reader();

	// Mutex protected public methods
	virtual bool StartThread( int port = 0 ) = 0;
	virtual void StopThread( void ) = 0;
	Frame* GetFrame( void );
//	int GetOutQueueSize( void )
//	{
//		return frames.size();
////		return outFrames.size();
//	}

	// These two public methods are not mutex protected
	virtual bool Open( int port = 0 ) = 0;
	virtual void Close( void ) = 0;

//	bool WaitForAction( int seconds = 0 );
	void TriggerAction( );

	virtual bool StartReceive( void ) = 0;
	virtual void StopReceive( void ) = 0;

protected:
	/// the iso channel we listen to (typically == 63)
	int	channel;

	/// contains information about our thread after calling StartThread
	pthread_t thread;

	/// this mutex protects capture related variables that could possibly
	/// accessed from two threads at the same time
	pthread_mutex_t mutex;

	// This condition and mutex are used to indicate when new frames are
	// received
	pthread_mutex_t condition_mutex;
	pthread_cond_t condition;

	/// A state variable for starting and stopping thread
	bool isRunning;
};


#ifdef HAVE_IEC61883
class iec61883Reader: public IEEE1394Reader
{
private:
	/// the handle to libraw1394
	raw1394handle_t m_handle;

	/// the handle to libiec61883
	iec61883_dv_fb_t m_iec61883dv;

public:

	iec61883Reader( int channel = 63, int buffers = 50 );
	~iec61883Reader();

	bool Open( int port );
	void Close( void );
	bool StartReceive( void );
	void StopReceive( void );
	bool StartThread( int port );
	void StopThread( void );
	int Handler( int length, int complete, unsigned char *data );
	void *Thread();
	void ResetHandler( void );

private:
	static int ResetHandlerProxy( raw1394handle_t handle, unsigned int generation );
	static int HandlerProxy( unsigned char *data, int length, int complete, 
		void *callback_data );
	static void* ThreadProxy( void *arg );
};
#endif


#ifdef HAVE_DV1394

class dv1394Reader: public IEEE1394Reader
{
private:

	unsigned char *m_dv1394_map;
	int m_dv1394_fd;

public:

	dv1394Reader( int chn = 63, int frames = 50 );
	~dv1394Reader();

	bool Open( int port );
	void Close( void );
	bool StartReceive( void );
	void StopReceive( void );
	bool StartThread( int port ); // port ignored
	void StopThread( void );
	void* Thread( );

private:
	bool Handler( int handle );
	static void* ThreadProxy( void *arg );
};

#endif


class AVC
{
private:
	/// the interface card to use (typically == 0)
	int port;
	int totalPorts;

	/// this mutex protects avc related variables that could possibly
	/// accessed from two threads at the same time
	pthread_mutex_t avc_mutex;

	/// the handle to the ieee1394 subsystem
	raw1394handle_t avc_handle;

public:
	AVC( void );
	~AVC();

	int isPhyIDValid( int id );
	unsigned int TransportStatus( int id );
	int getNodeId( const char *guid );
	int getPort( ) const
	{
		return port;
	}

//	static int discoverAVC( int* port, octlet_t* guid );
//	static int getChannel( int port, int node );
	
private:
	static int ResetHandler( raw1394handle_t handle, unsigned int generation );

};

class IEEE1394Writer : public IEEE1394
{
protected:
	bool m_isInitialised;
	unsigned int m_channel;
	unsigned int m_nBuffers;
	deque< Frame* > m_deque;
	pthread_t m_thread;

	/// this mutex protects capture related variables that could possibly
	/// accessed from two threads at the same time
	pthread_mutex_t m_dequeMutex;

	/// This condition and mutex are used to indicate when new frames are
	/// received
	pthread_mutex_t m_conditionMutex;
	pthread_cond_t m_condition;

	/// A state variable for starting and stopping thread
	bool m_isRunning;

public:
	IEEE1394Writer();
	virtual ~IEEE1394Writer();

//	Frame *InternalGetFrame();
	void StartThread();
	void StopThread();
	int WaitForAction( bool isBlocking, int seconds = 1 );
	void TriggerAction();

	virtual bool SendFrame( Frame &frame, bool isBlocking = true ) = 0;
	virtual bool isValid() = 0;
	virtual void* Thread() = 0;

private:
	static void* ThreadProxy( void *arg );
};


#ifdef HAVE_IEC61883
class iec61883Writer : public IEEE1394Writer
{
private:
	int m_port;
	raw1394handle_t m_handle;
	iec61883_dv_t m_iec61883dv;
	unsigned char* m_data;
	int m_index;
	bool m_isSilent;
	bool m_isReset;

public:

	iec61883Writer( int port, unsigned int channel, unsigned int nBuffers );
	~iec61883Writer();
	bool SendFrame( Frame &frame, bool isBlocking = true );
	bool isValid( void )
	{
		return ( !m_isInitialised || ( m_isInitialised && m_iec61883dv != NULL ) );
	}
	int Handler( unsigned char *data, int n_dif_blocks, unsigned int dropped );
	void* Thread();
	void ResetHandler( void );

private:
	bool Open( bool isPAL );
	static int ResetHandlerProxy( raw1394handle_t handle, unsigned int generation );
	static int HandlerProxy( unsigned char *data, int n_dif_blocks,
		unsigned int dropped, void *callback_data);
};
#endif


#ifdef HAVE_DV1394
class dv1394Writer : public IEEE1394Writer
{
private:
	int m_fd;
	unsigned int m_channel;
	unsigned int m_cip_n;
	unsigned int m_cip_d;
	unsigned int m_syt_offset;
	bool m_isSilent;

public:

	dv1394Writer( string device, unsigned int channel, unsigned int nBuffers,
	              unsigned int cip_n, unsigned int cip_d, unsigned int syt_offset );
	~dv1394Writer();
	bool SendFrame( Frame &frame, bool isBlocking = true );
	bool isValid( void )
	{
		return ( m_fd >= 0 );
	}
	void* Thread();
};
#endif

#endif
